﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace MMD_Morph_NoteHelp
{
    // ===================== 
    //    获取控件API和监控
    // =====================

    /// <summary>
    /// 用于封装要保存的表情数据
    /// </summary>
    public class MorphData
    {
        public string ModelName { get; set; } // 模型名称
        public Dictionary<string, string> Morphs { get; set; }  // 表情名称和数值的字典

        public MorphData()
        {
            Morphs = new Dictionary<string, string>();
        }
    }

    public class ControlMonitorService : IDisposable
    {
        private Dictionary<string, string> _morphCache = new Dictionary<string, string>(); // 用于记录当前模型的表情数据
        private string _currentModelName = ""; // 用于监测模型是否切换

        // 内部类：用于存储每个槽位的状态信息
        private class SlotInfo
        {
            public string Choice { get; set; } = ""; // 选择的表情名称
            public string Value { get; set; } = ""; // 数值
            public string Note { get; set; } = ""; // 注释备注
        }

        // === 定义精确的目标控件 ===
        private class TargetSlot
        {
            public string Key { get; } // 槽位标识
            public string TitleClassNN { get; } // 选择框 ClassNN
            public string ValueClassNN { get; } // 数值框 ClassNN
            public TargetSlot(string key, string title, string value)
            {
                Key = key; TitleClassNN = title; ValueClassNN = value;
            }
        }

        // 使用 AutohotKey 的 ClassNN 来定义4个表情操作槽位
        private readonly List<TargetSlot> _facialSlots = new List<TargetSlot>
        {
            new TargetSlot("Slot1", "ComboBox10", "Edit21"), // 左上角-目
            new TargetSlot("Slot2", "ComboBox12", "Edit23"), // 右上角-リップ
            new TargetSlot("Slot3", "ComboBox11", "Edit22"), // 左下角-まゆ
            new TargetSlot("Slot4", "ComboBox13", "Edit24")  // 右下角-その他
        };

        // === 维护语言状态 ===
        private CultureInfo _currentCulture;
        private readonly MainForm mainForm;
        private System.Threading.Timer timer;
        private int intervalMs = 200; // 提高响应速度
        private readonly string modelListPath;

        // 状态管理
        private readonly Dictionary<string, SlotInfo> _lastSlotInfo = new Dictionary<string, SlotInfo>();
        private string _activeSlotKey = null;

        // 目标窗口句柄
        private IntPtr targetHwnd = IntPtr.Zero;
        private int hwndLastCheckTick = 0; // 每次OnTick时递增
        private int hwndRefreshTicks = 25; // 5秒刷新

        public string TargetWindowTitle { get; set; } = "MikuMikuDance"; // MikuMikuDance窗口标题关键字
        public string TargetProcessName { get; set; } = null;

        // UI 防闪烁
        private string _lastUiChoice = "---", _lastUiValue = "---", _lastUiNote = "---";

        // 构造函数读取ini
        public ControlMonitorService(MainForm form)
        {
            this.mainForm = form ?? throw new ArgumentNullException(nameof(form));
            string p = "ModelList.ini";
            if (!File.Exists(p))
            {
                var alt = Path.Combine("config", "ModelList.ini"); // 配置文件：config/ModelList.ini
                if (File.Exists(alt)) p = alt;
            }
            modelListPath = p;

            // 初始化状态字典
            foreach (var slot in _facialSlots)
            {
                _lastSlotInfo[slot.Key] = new SlotInfo();
            }

            LanguageManager.OnLanguageChanged += OnLanguageChanged; // 在构造函数中订阅语言变更事件
            _currentCulture = Thread.CurrentThread.CurrentUICulture; // 初始化当前语言
        }

        // === 语言变更事件的处理器 ===
        private void OnLanguageChanged()
        {
            _currentCulture = Thread.CurrentThread.CurrentUICulture; // 这个方法会在UI线程上被调用，所以可以安全地获取最新的语言设置
            TriggerUpdate();  // 语言变了，立即触发一次数据刷新
        }

        public void StartMonitoring()
        {
            StopMonitoring();
            timer = new System.Threading.Timer(OnTick, null, 0, intervalMs);
        }

        public void StopMonitoring()
        {
            timer?.Dispose();
            timer = null;
        }

        // === 公共的 Dispose 方法用于清理订阅 ===
        public void Dispose()
        {
            LanguageManager.OnLanguageChanged -= OnLanguageChanged;
            StopMonitoring();
        }

        public void TriggerUpdate()
        {
            // 重置定时器，让它在0毫秒后立即触发一次OnTick，然后恢复正常的间隔
            timer?.Change(0, intervalMs);
        }

        // === 定时器回调 ===
        private void OnTick(object state)
        {
            try
            {
                hwndLastCheckTick++;
                if (targetHwnd == IntPtr.Zero || !IsWindow(targetHwnd) || hwndLastCheckTick >= hwndRefreshTicks)
                {
                    targetHwnd = FindTargetWindow();
                    hwndLastCheckTick = 0;
                }

                if (targetHwnd == IntPtr.Zero) return;

                ProcessTargetWindow(targetHwnd);
            }
            catch (Exception)
            {
                // 忽略定时器中的偶发错误，保持稳定性
            }
        }

        // === 获取当前表情数据 ===
        public MorphData GetCurrentMorphData()
        {
            if (string.IsNullOrEmpty(_currentModelName) || _morphCache.Count == 0)
            {
                return null; // 如果还没有任何记录，则返回null
            }

            return new MorphData
            {
                ModelName = _currentModelName,
                Morphs = new Dictionary<string, string>(_morphCache)
            };
        }

        /// <summary>
        /// 将指定的表情数据应用到 MMD 窗口
        /// </summary>
        /// <param name="data">包含表情数据的对象</param>
        /// <returns>返回应用失败的表情列表，如果全部成功则为空列表</returns>
        public async Task<List<string>> ApplyMorphData(MorphData data, Action<int, int> progressCallback = null, CancellationToken cancellationToken = default)
        {
            var failedMorphs = new List<string>();
            if (targetHwnd == IntPtr.Zero || data == null || data.Morphs.Count == 0) return failedMorphs;

            // 定义四个表情操作槽位的 ClassNN
            var toolSlots = new[]
            {
                new { Combo = "ComboBox10", Edit = "Edit21" }, // 左上角-目
                new { Combo = "ComboBox12", Edit = "Edit23" }, // 右上角-リップ
                new { Combo = "ComboBox11", Edit = "Edit22" }, // 左下角-まゆ
                new { Combo = "ComboBox13", Edit = "Edit24" } // 右下角-その他
            };

            // 预先获取所有工具的句柄
            var slotHwnds = toolSlots.Select(slot => new {
                ComboHwnd = FindControlByClassNN(targetHwnd, slot.Combo), // 表情列表
                EditClassNN = slot.Edit // 数值，我们现在传递ClassNN字符串给AHK
            }).ToList();

            // 如果任何一个都找不到，就无法工作
            if (slotHwnds.Any(h => h.ComboHwnd == IntPtr.Zero))
            {
                MessageBox.Show(L10n.Get("Error_MMDControlsNotFound")); // 无法找到MMD的表情下拉框控件，应用功能无法执行。
                return data.Morphs.Keys.ToList();
            }

            int total = data.Morphs.Count;
            int current = 0;

            // === 寻找表情 ===
            foreach (var morph in data.Morphs)
            {
                // 检查是否取消
                cancellationToken.ThrowIfCancellationRequested();

                current++;
                string morphName = morph.Key;
                string morphValue = morph.Value;
                progressCallback?.Invoke(current, total);

                bool successThisMorph = false;

                // 寻找窗口
                foreach (var slot in slotHwnds)
                {
                    // 应用数据时，我们仍然需要精确的 SelectComboBoxItem 功能
                    if (Win32Helper.SelectComboBoxItem(slot.ComboHwnd, morphName))
                    {
                        await Task.Delay(100); // 准备时间，毫秒单位，根据电脑性能不同可能会要再调整数值
                        await Win32Helper.SetValueWithAHK(slot.EditClassNN, morphValue);
                        successThisMorph = true;
                        await Task.Delay(20); // 在处理完一个表情后，增加一个微小的延迟
                        break; // 任务完成，跳出循环
                    }
                }
                if (!successThisMorph) failedMorphs.Add(morphName);
            }

            // === 所有部署完毕后 ===
            if (failedMorphs.Count < total) // 只要有任何一个成功了，就值得注册
            {
                cancellationToken.ThrowIfCancellationRequested(); // 再次检查取消
                await Win32Helper.PressHKeyWithAHK();  // 调用AHK脚本，按下H键，触发MMD更新
            }

            return failedMorphs;
        }

        // === 核心逻辑：直接使用ClassNN获取所有表情位 ===
        private void ProcessTargetWindow(IntPtr hwndToUse)
        {
            // 使用回归 v0.1 的稳定文本获取方式
            // 1. 获取模型选择 ComboBox3
            string modelName = GetTextByClassNN(hwndToUse, "ComboBox3");
            if (string.IsNullOrEmpty(modelName)) return;

            // 2. 检查模型是否已切换
            if (_currentModelName != modelName)
            {
                _morphCache.Clear();   // 如果切换了模型，就清空记录，翻开新的一页！
                _currentModelName = modelName;
                Debug.WriteLine($"模型已切换为: {_currentModelName}, 表情记录已重置。");
                UpdateMainFormUI("", "", "");  // 模型切换时，立即清空UI
            }

            // 读取ModelList.ini，找到对应的ini配置文件路径
            var mappings = ReadAllKeysFromIni();
            string iniPath = null;
            foreach (var kv in mappings)
            {
                if (modelName.IndexOf(kv.Key, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    string path = kv.Value;
                    if (!Path.IsPathRooted(path))
                        path = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, path);
                    if (File.Exists(path))
                    {
                        iniPath = path;
                        break;
                    }
                }
            }

            // 如果找不到对应的ini配置文件，则确保UI是清空的，然后退出本次处理。
            if (iniPath == null)
            {
                UpdateMainFormUI("", "", "");   // 再次调用以确保UI是空的，防止在某些边缘情况下UI未被清空。
                return; // 直接返回，不再处理后续的槽位信息
            }

            var sections = ParseIniSections(iniPath);
            var changedSlotKeys = new List<string>();

            // 3. 遍历我们定义的4个槽位，读取它们的数据
            foreach (var slot in _facialSlots)
            {
                string choice = GetTextByClassNN(hwndToUse, slot.TitleClassNN)?.Trim();
                string value = GetTextByClassNN(hwndToUse, slot.ValueClassNN);

                // 如果读取失败，则跳过
                if (choice == null || value == null) continue;

                var lastInfo = _lastSlotInfo[slot.Key];

                // 核心：当选择项或数值发生变化时，认为此槽位被操作
                if (lastInfo.Choice != choice || lastInfo.Value != value)
                {
                    // 只要有任何变化，就更新或添加记录
                    if (!string.IsNullOrEmpty(choice))
                    {
                        _morphCache[choice] = value;
                    }

                    string note = "";

                    // ====== v0.2：新增英文名备选 ========
                    // 第一步：优先使用表情名直接匹配节名
                    string matchedSection = sections.FirstOrDefault(s => s.Equals(choice, StringComparison.OrdinalIgnoreCase));

                    // 第二步：如果直接匹配失败，则遍历所有节，查找 Morph_EN 的值
                    if (matchedSection == null)
                    {
                        foreach (var section in sections)
                        {
                            // 读取当前节的 Morph_EN 值
                            string morphEnValue = ReadIniValueUtf8(iniPath, section, "Morph_EN");

                            // 如果 Morph_EN 的值与当前表情名匹配
                            if (!string.IsNullOrEmpty(morphEnValue) && morphEnValue.Equals(choice, StringComparison.OrdinalIgnoreCase))
                            {
                                matchedSection = section; // 将当前节作为匹配到的节
                                break; // 找到后立即跳出循环
                            }
                        }
                    }

                    // 第三步：如果通过以上任一方式找到了匹配的节，就读取注释
                    if (matchedSection != null)
                    {
                        string langKey = NormalizeCultureKey(_currentCulture.Name);
                        note = ReadIniValueUtf8(iniPath, matchedSection, langKey) ?? ReadIniValueUtf8(iniPath, matchedSection, "en") ?? "";
                    }

                    // 更新该槽位的最新状态
                    var currentInfo = new SlotInfo { Choice = choice, Value = value, Note = note };
                    _lastSlotInfo[slot.Key] = currentInfo;
                    changedSlotKeys.Add(slot.Key);
                }
            }

            // 4. 如果有槽位发生变化，则更新活动槽位
            if (changedSlotKeys.Any())
            {
                _activeSlotKey = changedSlotKeys.Last();
            }

            // 5. 根据活动槽位更新UI
            if (_activeSlotKey != null && _lastSlotInfo.ContainsKey(_activeSlotKey))
            {
                var infoToDisplay = _lastSlotInfo[_activeSlotKey];
                UpdateMainFormUI(infoToDisplay.Choice, infoToDisplay.Value, infoToDisplay.Note);
            }
        }

        // === 更新主窗口UI ===
        private void UpdateMainFormUI(string choice, string value, string note)
        {
            choice = choice ?? ""; value = value ?? ""; note = note ?? "";
            if (_lastUiChoice == choice && _lastUiValue == value && _lastUiNote == note) return;

            _lastUiChoice = choice; _lastUiValue = value; _lastUiNote = note;

            if (!mainForm.IsDisposed && mainForm.IsHandleCreated)
            {
                // 使用 v0.1 的方式调用 UI 更新，可能稍微稳定一点
                mainForm.BeginInvoke(new Action(() =>
                {
                    mainForm.ChoiceDisplay.Text = choice;
                    mainForm.ValueSize.Text = value;
                    mainForm.NoteContent.Text = note;
                }));
            }
        }

        // === 添加日志方法 ===
        //private static void LogToFile(string message)
        //{
        //    try
        //    {
        //        string logPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "debug_log.txt");
        //        string logMessage = $"{DateTime.Now:yyyy-MM-dd HH:mm:ss.fff} - {message}{Environment.NewLine}";
        //        File.AppendAllText(logPath, logMessage);
        //    }
        //    catch
        //    {
        //        // 忽略日志写入错误
        //    }
        //}

        #region --- 关键：可靠的 ClassNN 获取方法 (回归 v0.1 稳定版) ---

        /// <summary>
        /// 通过 ClassNN 获取控件文本。
        /// 回归 v0.1 版本逻辑：只使用最基础的 GetWindowText/SendMessage 获取当前显示的文本。
        /// 放弃了不稳定的 GetComboBoxSelectedItem，解决了点击时的闪烁问题。
        /// </summary>
        private string GetTextByClassNN(IntPtr parentHwnd, string classNN)
        {
            // LogToFile($"开始获取控件文本: {classNN}");

            IntPtr controlHwnd = FindControlByClassNN(parentHwnd, classNN);
            if (controlHwnd == IntPtr.Zero)
            {
                return null; // 返回null以区分空字符串
            }
            // 直接获取显示的文本，不关心它是不是 ComboBox，这是最稳定的方式
            return GetWindowText(controlHwnd) ?? GetWindowTextBySendMessage(controlHwnd) ?? "";
        }

        /// <summary>
        /// 精确查找符合ClassNN的控件句柄
        /// </summary>
        private IntPtr FindControlByClassNN(IntPtr parent, string classNN)
        {
            if (parent == IntPtr.Zero || string.IsNullOrEmpty(classNN)) return IntPtr.Zero;

            var match = Regex.Match(classNN, @"^([A-Za-z_]+)(\d+)$");
            if (!match.Success) return IntPtr.Zero;

            string baseClass = match.Groups[1].Value;
            if (!int.TryParse(match.Groups[2].Value, out int targetIndex) || targetIndex <= 0) return IntPtr.Zero;

            var allDescendants = GetAllDescendantWindows(parent);
            int currentIndex = 0;

            foreach (var hwnd in allDescendants)
            {
                var sb = new StringBuilder(256);
                GetClassNameRaw(hwnd, sb, sb.Capacity);

                // 注意：AHK的匹配是'Starts with'，这里用 IndexOf 来模拟
                if (sb.ToString().IndexOf(baseClass, StringComparison.OrdinalIgnoreCase) >= 0)
                {
                    currentIndex++;
                    if (currentIndex == targetIndex)
                    {
                        return hwnd;
                    }
                }
            }
            return IntPtr.Zero;
        }

        /// <summary>
        /// 获取一个窗口的所有后代控件，顺序与Win32枚举顺序一致
        /// </summary>
        private List<IntPtr> GetAllDescendantWindows(IntPtr parent)
        {
            var result = new List<IntPtr>();
            EnumChildWindows(parent, (hwnd, lParam) =>
            {
                result.Add(hwnd);
                result.AddRange(GetAllDescendantWindows(hwnd));  // 递归添加子孙
                return true;
            }, IntPtr.Zero);
            return result;
        }

        #endregion

        #region --- 辅助方法 ---
        private IntPtr FindTargetWindow()
        {
            try
            {
                if (!string.IsNullOrEmpty(TargetWindowTitle))
                {
                    var byTitle = FindWindowByTitleContains(TargetWindowTitle);
                    if (byTitle != IntPtr.Zero) return byTitle;
                }
                if (!string.IsNullOrEmpty(TargetProcessName))
                {
                    var byProc = FindWindowByProcessName(TargetProcessName);
                    if (byProc != IntPtr.Zero) return byProc;
                }
            }
            catch { }
            return IntPtr.Zero;
        }

        private IntPtr FindWindowByTitleContains(string titlePart)
        {
            IntPtr found = IntPtr.Zero;
            EnumWindows((hwnd, lParam) =>
            {
                int len = GetWindowTextLengthRaw(hwnd);
                if (len > 0 && IsWindowVisible(hwnd))
                {
                    var sb = new StringBuilder(len + 1);
                    GetWindowTextRaw(hwnd, sb, sb.Capacity);
                    if (sb.ToString().IndexOf(titlePart, StringComparison.OrdinalIgnoreCase) >= 0)
                    {
                        found = hwnd;
                        return false;
                    }
                }
                return true;
            }, IntPtr.Zero);
            return found;
        }

        // 通过进程名查找窗口
        private IntPtr FindWindowByProcessName(string procName)
        {
            string clean = procName.EndsWith(".exe", StringComparison.OrdinalIgnoreCase) ? procName.Substring(0, procName.Length - 4) : procName;
            clean = clean.Trim();
            IntPtr found = IntPtr.Zero;
            EnumWindows((hwnd, lParam) =>
            {
                if (!IsWindowVisible(hwnd)) return true;
                GetWindowThreadProcessId(hwnd, out uint pid);
                try
                {
                    var p = Process.GetProcessById((int)pid);
                    if (p != null && string.Equals(p.ProcessName, clean, StringComparison.OrdinalIgnoreCase))
                    {
                        found = hwnd;
                        return false;
                    }
                }
                catch { }
                return true;
            }, IntPtr.Zero);
            return found;
        }

        // 读取ini文件的所有键值对
        private Dictionary<string, string> ReadAllKeysFromIni()
        {
            var result = new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
            try
            {
                if (string.IsNullOrEmpty(modelListPath) || !File.Exists(modelListPath)) return result;
                var lines = File.ReadAllLines(modelListPath, Encoding.UTF8);
                foreach (var line in lines)
                {
                    var l = line.Trim();
                    if (string.IsNullOrEmpty(l) || l.StartsWith(";") || l.StartsWith("#") || (l.StartsWith("[") && l.EndsWith("]")) || !l.Contains("=")) continue;
                    int idx = l.IndexOf('=');
                    string k = l.Substring(0, idx).Trim();
                    string v = l.Substring(idx + 1).Trim();
                    if (!result.ContainsKey(k)) result[k] = v;
                }
            }
            catch { }
            return result;
        }

        // 解析ini文件中的所有节名称
        private List<string> ParseIniSections(string iniPath)
        {
            var list = new List<string>();
            try
            {
                string txt = File.ReadAllText(iniPath, Encoding.UTF8);
                foreach (Match m in Regex.Matches(txt, @"\[(.+?)\]"))
                {
                    var s = m.Groups[1].Value.Trim();
                    if (!string.IsNullOrEmpty(s) && !list.Contains(s)) list.Add(s);
                }
            }
            catch { }
            return list;
        }

        // 读取ini中特定节和键的值，支持UTF-8
        /* 例子：
        [SunLight+]
            en = Overall light intensity adjustment of the main light source
                   Increase light intensity
            zh-CN = 主光源的整体光强调节
                   增加光照的强度
            zh-TW = 主光源的整體光強調節
                   增加光照的強度
            ja = 主光源の全体光強度の調整
                   光の強さを強める 
         */
        private string ReadIniValueUtf8(string iniPath, string section, string key)
        {
            try
            {
                if (!File.Exists(iniPath)) return string.Empty;
                string content = File.ReadAllText(iniPath, Encoding.UTF8);
                var secMatch = Regex.Match(content, @"\[" + Regex.Escape(section) + @"\](.*?)(?=\r?\n\[|$)", RegexOptions.Singleline | RegexOptions.IgnoreCase);
                if (!secMatch.Success) return string.Empty;
                var kvMatch = Regex.Match(secMatch.Groups[1].Value, @"^\s*" + Regex.Escape(key) + @"\s*=\s*(.*)$", RegexOptions.Multiline | RegexOptions.IgnoreCase);
                return kvMatch.Success ? kvMatch.Groups[1].Value.Trim() : string.Empty;
            }
            catch { return string.Empty; }
        }

        // 语言对应键名规范化
        private string NormalizeCultureKey(string cultureName)
        {
            if (string.IsNullOrEmpty(cultureName)) return "en";
            if (cultureName.StartsWith("zh-CN", StringComparison.OrdinalIgnoreCase)) return "zh-CN";
            if (cultureName.StartsWith("zh-TW", StringComparison.OrdinalIgnoreCase)) return "zh-TW";
            if (cultureName.StartsWith("ja", StringComparison.OrdinalIgnoreCase)) return "ja";
            if (cultureName.StartsWith("en", StringComparison.OrdinalIgnoreCase)) return "en";
            return cultureName;
        }
        #endregion

        #region --- Win32 API 本地声明 (保持独立性) ---
        private delegate bool EnumChildProc(IntPtr hwnd, IntPtr lParam);
        private delegate bool EnumWindowsProc(IntPtr hWnd, IntPtr lParam);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetClassName(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount);
        private static int GetClassNameRaw(IntPtr hWnd, StringBuilder lpClassName, int nMaxCount) => GetClassName(hWnd, lpClassName, nMaxCount);

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetWindowText(IntPtr hWnd, StringBuilder lpString, int nMaxCount);
        private static int GetWindowTextRaw(IntPtr hWnd, StringBuilder lpString, int nMaxCount) => GetWindowText(hWnd, lpString, nMaxCount);

        // v0.1 风格的本地 GetWindowText 包装器
        private string GetWindowText(IntPtr hwnd)
        {
            // v0.1 使用了更大的缓冲区 (1024)，这里恢复它以防万一
            var sb = new StringBuilder(1024);
            return GetWindowTextRaw(hwnd, sb, sb.Capacity) > 0 ? sb.ToString() : null;
        }

        [DllImport("user32.dll", SetLastError = true, CharSet = CharSet.Auto)]
        private static extern int GetWindowTextLength(IntPtr hWnd);
        private static int GetWindowTextLengthRaw(IntPtr hWnd) => GetWindowTextLength(hWnd);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool EnumChildWindows(IntPtr hwndParent, EnumChildProc lpEnumFunc, IntPtr lParam);

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        private static extern bool EnumWindows(EnumWindowsProc lpEnumFunc, IntPtr lParam);

        [DllImport("user32.dll")] private static extern bool IsWindow(IntPtr hWnd);
        [DllImport("user32.dll")] private static extern bool IsWindowVisible(IntPtr hWnd);
        [DllImport("user32.dll")] private static extern uint GetWindowThreadProcessId(IntPtr hWnd, out uint lpdwProcessId);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, IntPtr lParam);

        [DllImport("user32.dll", CharSet = CharSet.Auto)]
        private static extern int SendMessage(IntPtr hWnd, uint Msg, int wParam, StringBuilder lParam);

        // 通过 SendMessage 获取窗口文本（备用方法）
        private string GetWindowTextBySendMessage(IntPtr hwnd)
        {
            const uint WM_GETTEXTLENGTH = 0x000E;
            const uint WM_GETTEXT = 0x000D;
            int len = SendMessage(hwnd, WM_GETTEXTLENGTH, 0, IntPtr.Zero);
            if (len <= 0) return null;
            var sb = new StringBuilder(len + 1);
            SendMessage(hwnd, WM_GETTEXT, sb.Capacity, sb);
            return sb.ToString();
        }
        #endregion

        // ========= END =============
    }
}